package com.wengu.algorithm.dto;

import lombok.Data;

import java.io.Serializable;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.math.MathContext;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

import static cn.hutool.core.util.NumberUtil.factorial;

@Data
public class TaskAlg implements Serializable {

    private static final long serialVersionUID = 1345547L;

    public Long id;
    /**
     * 位置X
     */
    public int taskX;
    /**
     * 位置Y
     */
    public int taskY;

    /**
     * 任务到达时间（相对于场景开始时间持续到了多少秒）
     */
    public double arrivalTime;
    /**
     * 任务持续时间（秒）
     */
    public double duration;
    /**
     * 任务结束时间（相对于场景开始时间持续到了多少秒）
     */
    public double endTime;
    /**
     * 执行成本
     */
    public double executionCost;
    /**
     * 上一个任务到当前任务的旅行成本
     */
    public double distanceCost = 0;
    /**
     * 该任务的总质量
     */
    public double totalF = 0;
    /**
     * 成功概率
     */
    public double successProb;
    /**
     * 该任务被某用户执行后的质量增量
     */
    public double deltaF;
    /**
     * 已执行次数
     */
    public int k = 0;
//    public Integer index;

    // 阶乘计算结果缓存，避免重复计算
    public static Map<Integer, BigInteger> factorialCache = new HashMap<>();
    // 组合数计算结果缓存，键格式为"n,k"
    public static Map<String, BigInteger> combinationCache = new HashMap<>();
    // 计算质量的缓存
    public Map<Integer, BigDecimal> fCache = new HashMap<>();
    // 高精度数学运算上下文，设置20位精度
    private MathContext mathContext = new MathContext(20);
    public static final int MAX_USER_COUNT = 8;
    private final double scalingFactor = 1 / Math.log(1 + MAX_USER_COUNT);
    // 组合数计算结果缓存，键格式为"n,k"
    public static Map<Integer, Double> logCache = new HashMap<>();

    public double calDeltaFCommon() {
        return calDeltaF();
    }

    /**
     * 计算质量增量ΔF，即当前k值与k-1时的F值之差
     * @return 质量增量ΔF的double值
     */
    public double calDeltaF() {
        BigDecimal fCurrent = calculateF(k);
        BigDecimal fNext = calculateF(k+1);
        return fNext.subtract(fCurrent).doubleValue();
    }
    /**
     * 计算质量增量ΔF，即当前k值与k-1时的F值之差
     * @return 质量增量ΔF的double值
     */
    public double calDeltaFv2() {
        double fCurrent = calculateQuality(k);
        double fNext = calculateQuality(k+1);
        return fNext - fCurrent;
    }
    /**
     * 计算质量增量ΔF，即当前k值与k-1时的F值之差
     * @return 质量增量ΔF的double值
     */
    public double calDeltaFv3() {
        if (k >= MAX_USER_COUNT) {
            return 0;
        }
        return 1.0 / MAX_USER_COUNT;
    }


    /**
     * 计算指定用户数时的任务质量
     * @param userCount 已参与用户数 K
     * @return 任务质量值 [0, targetMaxQuality]
     */
    public double calculateQuality(int userCount) {
        if (userCount < 0) {
            throw new IllegalArgumentException("用户数不能为负数");
        }
        Double cache = logCache.get(userCount);
        if (cache != null){
            return cache;
        }
        if (userCount >= MAX_USER_COUNT) {
            return 1;
        }
        double kQuality = scalingFactor * Math.log(1 + userCount);
        logCache.put(userCount,kQuality);
        return kQuality;
    }

    /**
     * 计算F值，根据k的奇偶性采用不同计算策略
     * @param k 执行次数
     * @return F值的BigDecimal表示，保证高精度
     */
    public BigDecimal calculateF(int k) {
        // 边界条件处理
        if (k < 0) {
            return BigDecimal.ZERO;
        }
        BigDecimal calF = fCache.get(k);
        if (calF != null){
            return calF;
        }
        // 根据公式(2)实现F计算
        if (k % 2 == 1 || k == 0) {
            calF = calculateA(k);
            fCache.put(k,calF);
            return calF;
        } else {
            // 偶数情况采用前后两个奇数点的平均值
            BigDecimal aPrev = calculateA(k-1);
            BigDecimal aNext = calculateA(k+1);
            calF = aPrev.add(aNext).divide(new BigDecimal("2"), mathContext);
            fCache.put(k,calF);
            return calF;
        }
    }

    /**
     * 计算A值，即成功概率的累积分布函数
     * @param k 执行次数
     * @return A值的BigDecimal表示
     */
    private BigDecimal calculateA(int k) {
        if (k < 0) {
            return BigDecimal.ZERO;
        }
        if (k == 0) {
            return new BigDecimal("0.5");
        }

        BigDecimal sum = BigDecimal.ZERO;
        // 确定求和起始点，根据k的奇偶性有所不同
        int start = (k % 2 == 1) ? (k+1)/2 : k/2 + 1;

        // 预计算常用值，避免重复创建对象
        BigDecimal successProbBD = BigDecimal.valueOf(successProb);
        BigDecimal oneMinusSuccessProbBD = BigDecimal.valueOf(1 - successProb);

        // 计算主要求和部分
        for (int r = start; r <= k; r++) {
            BigInteger comb = combination(k, r);
            BigDecimal term = new BigDecimal(comb)
                    .multiply(successProbBD.pow(r, mathContext))
                    .multiply(oneMinusSuccessProbBD.pow(k-r, mathContext));
            sum = sum.add(term);
        }

        // 偶数k时需要额外处理中间项
        if (k % 2 == 0) {
            BigInteger midComb = combination(k, k/2);
            BigDecimal midTerm = new BigDecimal(midComb)
                    .multiply(successProbBD.pow(k/2, mathContext))
                    .multiply(oneMinusSuccessProbBD.pow(k/2, mathContext));
            sum = sum.add(midTerm.multiply(new BigDecimal("0.5"), mathContext));
        }
        return sum;
    }

    /**
     * 计算组合数C(n,k)，使用缓存优化
     * @param n 总数
     * @param k 选取数
     * @return 组合数C(n,k)的BigInteger值
     */
    private BigInteger combination(int n, int k) {
        // 边界条件快速返回
        if (k == 0 || k == n) {
            return BigInteger.ONE;
        }
        if (k == 1 || k == n-1) {
            return BigInteger.valueOf(n);
        }

        // 使用对称性减少计算量
        k = Math.min(k, n - k);
        String cacheKey = n + "," + k;

        // 检查缓存
        BigInteger cached = combinationCache.get(cacheKey);
        if (cached != null) {
            return cached;
        }

        // 计算并缓存结果
        BigInteger result = factorial(n).divide(factorial(k).multiply(factorial(n - k)));
        combinationCache.put(cacheKey, result);
        return result;
    }

    /**
     * 计算阶乘n!，使用缓存优化
     * @param n 要计算阶乘的数
     * @return n!的BigInteger值
     * @throws IllegalArgumentException 如果n为负数
     */
    public BigInteger factorial(int n) {
        if (n < 0) {
            throw new IllegalArgumentException("n must be non-negative, but got " + n);
        }
        if (n <= 1) {
            return BigInteger.ONE;
        }

        // 检查缓存
        BigInteger cached = factorialCache.get(n);
        if (cached != null) {
            return cached;
        }

        // 计算并缓存结果
        BigInteger result = BigInteger.ONE;
        for (int i = 2; i <= n; i++) {
            result = result.multiply(BigInteger.valueOf(i));
        }

        factorialCache.put(n, result);
        return result;
    }


    @Override
    public int hashCode() {
        return Objects.hash(id);  // 只基于 id 计算
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        TaskAlg taskAlg = (TaskAlg) o;
        return Objects.equals(id, taskAlg.id);  // 只比较 id
    }

    public static void main(String[] args) {
        TaskAlg taskAlg = new TaskAlg();
        for (int i = 0; i < 11; i++) {

            System.out.println("k:"+i+"，质量："+taskAlg.calculateQuality(i));
            taskAlg.k = i;
            System.out.println("k:"+taskAlg.k+"，质量增量："+taskAlg.calDeltaFv2());
        }
    }

}